/** CSC 460/560 Real Time Operating Systems - Mantis Cheng
 *	Fall 2007
 *	
 *	@author Scott Craig
 *
 *	A driver for sending debugging messages from one radio
 *	to another. Delivery is not guaranteed.
 *
 *  NB!: Must be compiled with optimisation at least -O2
 */

#include "radio.h"

/** Configuration data */
#define CFG_VECT_LEN		15
static uint8_t radio_cfg[CFG_VECT_LEN] =
{ 
	DATA2_W,    // byte 0
	DATA1_W,    // byte 1
	ADDR2,		// bytes 2 to 6
	ADDR1,		// bytes 7 to 11
	ADDRW_CRC,  // byte 12
	MODES,      // byte 13
	RFCH_RXEN   // byte 14
};


/** Buffer for sending and receiving lines destined for UART */
static char radio_buf[RADIO_BUF_LEN];

/* Forward decs */
static inline void delay_125ns(void);
static inline void delay_500ns(void);
static inline void delay_1us(void);
static void put_byte(uint8_t rfbyte);
static void set_standby_mode(void);
static void set_config_mode(void);
static void set_rxtx_mode(void);


/** Assumes CPU frequency is 8 MHz 
 */
static inline void delay_125ns(void) 
{
	__asm__ volatile (
		"nop\n\t"
		::
	);
}


/** Assumes CPU frequency is 8 MHz 
 */
static inline void delay_500ns(void) 
{
	__asm__ volatile (
		"nop\n\t"
		"nop\n\t"
		"nop\n\t"
		"nop\n\t"
		::
	);
}


/** Assumes CPU frequency is 8 MHz 
 */
static inline void delay_1us(void) 
{
	__asm__ volatile (
		"nop\n\t"
		"nop\n\t"
		"nop\n\t"
		"nop\n\t"
		"nop\n\t"
		"nop\n\t"
		"nop\n\t"
		"nop\n\t"
		::
	);
}


/**
 * \fn put_byte(uint8_t rfbyte)
 * \param rfbyte the byte to be loaded into the payload for transmission.
 * \return void.
 *              
 * Write one byte into the payload of the radio packet one bit at a time.
 * This is the lowest level write function.
 */
static void put_byte(uint8_t rfbyte)
{
	int shift;

	for(shift = 7; shift >= 0; --shift)
	{
		if(rfbyte & _BV(shift))
			DATA_HIGH();
		else
			DATA_LOW();

		//delay_500ns(); // Already takes that long at top of loop		
		CLK1_HIGH();
		delay_500ns();
		CLK1_LOW();
	}		
}


/**
 * \fn set_standby_mode(void)
 * \return void.
 *
 * Switch the radio into standby mode.
 */
static void set_standby_mode(void)
{
	// Disable external interrupt
	EIMSK &= ~_BV(INT4);

	CS_LOW();
	CE_LOW();

	CLK1_LOW();
	DATA_LOW();
}



/**
 * \fn set_config_mode(void)
 * \return void.
 *
 * Switch the radio into configuration mode.  This is only necessary when  
 * initializing the radio or when changing between radio RX and TX modes.
 */
static void set_config_mode(void)
{
	CE_LOW();

	// Set Data to output
	DATA_DDR |= _BV(DATA_PINNUM);

    DATA_LOW();
    CLK1_LOW();
    
    delay_125ns(); 	// Td = 50 ns

	CS_HIGH();
	
	_delay_us(5); 	// Tcs2data = 5 us
}


/**
 * \fn set_rxtx_mode(void)
 * \return void.
 *
 * Switch the radio into active mode.
 */
static void set_rxtx_mode(void)
{
	CS_LOW();
	delay_125ns(); // Td = 50 ns

	DATA_LOW();
    CLK1_LOW();

	CE_HIGH();
	_delay_us(5); // Tce2data = 5 us
}



/* PUBLIC FUNCTIONS */
/**
 *  \fn radio_init
 *
 *  \param channel A number corresponding to a legal channel.
 *  \param address A 16 bit integer (Eg. 0xABBA) to be this radio's ID.
 *  \param rx_enable 0 for transmit, anything else for receive.
 *  \return -1 if not a legal channel
 *
 *  This function is called to configure the radio. In BC, legal
 *  channel numbers are 0 to 82, or 101 to 119. the range 101 to 119
 *  is usually better, because to other channels compete with wireless
 *  LANs. If the channel is wrong, it will be set to DEFAULT_CHANNEL.
 *
 *  The address is a number in the packet. For this app, both the 
 *  sender and receiver will have the same address. 
 */
int radio_init(uint8_t channel, uint16_t address, uint8_t rx_enable)
{
    uint8_t rxen = (rx_enable > 0) ? 1 : 0;
    int bad_channel = 0;
    uint8_t* txbyte = radio_cfg;
    uint8_t count = CFG_VECT_LEN;

    // Set up config vector
    radio_cfg[10] = (uint8_t)(address >> 8);
    radio_cfg[11] = (uint8_t) address;

    if((channel > 82 && channel < 101) || channel > 119)
    {
        channel = DEFAULT_CHANNEL;
        bad_channel = -1;
    }
    radio_cfg[14] = (uint8_t)((channel << 1) | rxen);
    
    if(rx_enable)
    {
        // Rising edge trigger on INT4 (1,1) for data ready pin of radio
        EICRB |= (_BV(ISC41) | _BV(ISC40));
    }

	// Set up output pins
	CLK1_DDR |= _BV(CLK1_PINNUM);
	CE_DDR	|= _BV(CE_PINNUM);
	CS_DDR |= _BV(CS_PINNUM);
    DATA_DDR |= _BV(DATA_PINNUM);

	_delay_ms(3); // Tpd2sby = 3 ms (power-on-to-standby waiting time)

	set_config_mode();

    while(count)
    {
        put_byte(*txbyte);
        ++txbyte;
        --count;
    }

    set_standby_mode();

    // Set Data to input or output
    if(rx_enable)
    {
        DATA_DDR &= ~(_BV(DATA_PINNUM));        
    }
    else
    {
	    DATA_DDR |= _BV(DATA_PINNUM);
    }


    set_rxtx_mode();

    if(rx_enable)
    {
        EIMSK |= _BV(INT4);
    }

    return bad_channel;
}


/**
 *  \fn radio_print
 *
 *  \param  format printf style arguments
 *  \return number of bytes sent
 *
 *  A function to send a formatted string over the radio, printf style.
 *  The string is broken into packet sized chunks, which are sent.
 *  Delivery is not guaranteed. Pieces of the string may not
 *  arrive. The chunk size is PAYLOAD_BYTES, so strings
 *  shorter than that will either arrive or not. The maximum string
 *  length is RADIO_BUF_LEN.
 */
int radio_print(const char* format, ...)
{
    int bytes_written;
    int bytes_to_write;
    int bytes_this_packet;
    int bytes_left_over;
    char* buf_ptr = radio_buf;

    va_list args;
    va_start(args, format);

    bytes_written = vsnprintf(radio_buf, RADIO_BUF_LEN, format, args);

    if(bytes_written > RADIO_BUF_LEN) bytes_written = RADIO_BUF_LEN;

    va_end(args);

    bytes_to_write = bytes_written;
    buf_ptr = radio_buf;

    while(bytes_to_write)
    {
        set_rxtx_mode();
        
        if(bytes_to_write > PAYLOAD_BYTES)
        {
            bytes_this_packet = PAYLOAD_BYTES;
            bytes_left_over = 0;
            bytes_to_write -= PAYLOAD_BYTES;
        }
        else
        {
            bytes_this_packet = bytes_to_write;
            bytes_left_over = PAYLOAD_BYTES - bytes_this_packet;
            bytes_to_write = 0;
        }

        put_byte(radio_cfg[10]);
        put_byte(radio_cfg[11]);

        while(bytes_this_packet--)  //Payload
        {
            put_byte(*buf_ptr);
            ++ buf_ptr;
        }

        while(bytes_left_over--)  // Padding
        {
            put_byte((uint8_t)0);
        }

	    // start shockburst transmission
	    DATA_LOW();	
	    CE_LOW();
	    //_delay_us(500);	// Tsby2txSB + Toa = 195 us + 256 bits / 1 Mbps
        _delay_ms(DELAY_MS_BETWEEN_PACKETS);
    }

    return bytes_written;
}



/**
 * \fn uint8_t rf24g_get_byte(void)
 * \return uint8_t
 *
 * Read one byte from the payload of the radio packet one bit at a time.
 * This is the lowest level read function.
 */
uint8_t radio_get_byte(void)
{
	int shift;
	unsigned char rfbyte = 0x0;

	//_delay_us(1); // delay dr to clock > 50 ns // Already delayed that long anyway

	for(shift = 7; shift >= 0; --shift)
	{
		CLK1_HIGH();
		
		if (DATA_PIN & _BV(DATA_PINNUM))
			rfbyte |= _BV(shift);
		
		//delay_500ns(); // hold time > Thmin == 500 ns
        // The above instructions take that long

		CLK1_LOW();		

		//delay_500ns(); // Tclk2data = 50 ns
        // The above instruction takes that long
	}

	return rfbyte;
}






